#include <efi.h>

#define MAX_RECURSION			6
#define SCREEN_MIN_WIDTH		800
#define SCREEN_MIN_HEIGHT		600
#define STR_SIZE			256

/* mode = a number between 0 and GOP->Mode->MaxMode-1 */

struct sysinfo
{
	int		gfxMode, fbSize, fbX, fbY;
	void		*fb;
	/* mem map */
};

struct coord
{
	int		x, y;
};

struct colour
{
	uint8_t		r, g, b;
};

struct colour		white;

/* -------------------------------------------------------------- */

int valid(CHAR16 str[])
{
	...
	return 0;
}

int possible(int c)
{
	...
	return 0;
}
	
int numFromStr(CHAR16 str[])
{
	int	count, i;

	count = 0;
	i = 0;
	do
	{
		i = (i * 10) + str[count] - '0';
		count++;
	}
	while(str[count] != '\0');
	return count;
}

void askForMode(EFI_SYSTEM_TABLE ST, struct sysinfo *si)
{	
	CHAR16					s[STR_SIZE];

	do
	{	
		do
		{
			do
			{
				ST->ConOut->OutputString(ST->ConOut,
					L"Enter graphics mode:");
				ST->ConIn->Input(ST->ConIn, s); wrong???
			}
			while(!valid(s));
			si->gfxMode = numFromStr(s);
		}
		while(!possible(si->gfxMode));
		si->fb = GOP->FramebufferBase;
		si->fbSize = GOP->FramebufferSize;
		si->fbX = ...;
		si->fbY = ...;		
	}
	while(si->fbX < SCREEN_MIN_WIDTH || si->fbY <SCREEN_MIN_HEIGHT);
}

/* -------------------------------------------------------------- */

void sierpinski(struct coord c1, struct coord c2, int step)
{
	struct coord 		targetC1, targetC2, targetC3, c3, rectSize;
	int			midX, midY, diffX, diffY;
	
	rectSize.x = RECT_X_SIZE;
	rectSize.y = RECT_Y_SIZE;
	midX = (c1.x + c2.x)/2;
	midY = (c1.y + c2.y)/2;
	diffX = F*(c2.x - c1.x)/2;
	diffY = F*(c2.y - c1.y)/2;
	
	if (step == MAX_RECURSION)
	{
		c3.x = midX + diffX;
		c3.y = midY - diffX;
		DrawTriangle(c1, c2, c3);
	}
	else
	{
		targetC1.x = midX;
		targetC1.y = midY;
		targetC2.x = (3 * c1.x + c2.x) / 4 + diffY;
		targetC2.y = (3 * c1.y + c2.y) / 4 - diffX;
		targetC3.x = (c1.x + 3 * c2.x) / 4 + diffY;
		targetC3.y = (c1.y + 3 * c2.y) / 4 - diffX;
		
		sierpinski(c1, targetC1, step+1);
		sierpinski(targetC1, c2, step+1);
		sierpinski(targetC2, targetC3, step+1);
	}
}

void gfxDemo()
{
	white.r = 255;
	white.g = 255;
	white.b = 255;
	c1.x = 200;
	c1.y = 400;
	c2.x = 600;
	c2.y = 400;
	sierpinski(c1, c2, 0);
}

/* -------------------------------------------------------------- */

EFI_STATUS efi_main(EFI_HANDLE IH, EFI_SYSTEM_TABLE *ST)
{
	/* EFI_GUID				guid; */
	EFI_GRAPHICS_OUTPUT_PROTOCOL		GOP;
	struct sysinfo				sys;

	if(EFI_ERROR(ST->BootServices->LocateProtocol(
		EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, NULL, GOP)))
	{
		ST->ConOut->OutputString(ST->ConOut, L"No GOP.\n");
		return EFI_UNSUPPORTED;
	}
	askForMode(ST, &sys);
	
	GOP->SetMode(sys.gfxMode);
	
	/* Framebuffer demo: Sierpinski triangle */
	gfxDemo();
	
	/* Endless loop, left after 5 minutes (watchdog timer) */
	for(;;){}	
	
	/* Never reached. Make the compiler happy */
	return EFI_SUCCESS;
}

